package model;

import Network.ClientTCP;
import chataudio.Constant;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import observer.ActivityObserver;
import observer.Observable;
import observer.ListObserver;
import observer.UserObserver;

/**
 *
 * @author Romain
 */
public abstract class AbstractChatModel implements Observable {

    private ArrayList<ListObserver> listConnObserver = new ArrayList<ListObserver>();       // Liste Connection Observers
    private ArrayList<UserObserver> listUserObserver = new ArrayList<UserObserver>();       // Liste User Observers
    private ArrayList<ActivityObserver> listActivityObserver = new ArrayList<ActivityObserver>();       // Liste User Observers
    List<Connection> activeList = new ArrayList<Connection>();                  // Contient la liste des connections active
    List<Connection> waitingList = new ArrayList<Connection>();                 // Contient la liste des connections en attente
    List<User> UserList = new ArrayList<User>();                                // Contient la liste des utilisateurs
    private int currentUser = 0;
    private boolean enableAutodiscover = true;
    private soundSetting localSoundSetting = new soundSetting();
    private int portUDP = 2500;
    boolean lineActive = false;
    
    public abstract void AddConnToWaitList(Connection paramConn);
    
    public abstract void AddConnToActList(Connection paramConn);
    
    public abstract void RemoveConnWaitList(Connection paramConn);
    
    public abstract void RemoveConnActList(Connection paramConn);
    public abstract void UpdateList();
    
    public AbstractChatModel() throws FileNotFoundException, IOException, ClassNotFoundException {
        ouvrir();
    }
    
//******************************************************************************
//**********************METHODES OBSERVER***************************************
//******************************************************************************
    @Override
    public void addConnObserver(ListObserver obs) {
        this.listConnObserver.add(obs);
    }
    @Override
    public void addUserObserver(UserObserver obs) {
        this.listUserObserver.add(obs);
    }
    
    @Override
    public void notifyObserverWaitList(List<Connection> list) {
        for(ListObserver obs : listConnObserver){
            obs.updateWaitingList(list);
        }
    }
    
    @Override
    public void notifyObserverActList(List<Connection> list) {
        for(ListObserver obs : listConnObserver){
            obs.updateActiveList(list);
        }
    }
    
    @Override
    public void notifyObserverUserList(List<User> list){
        for(UserObserver obs : listUserObserver){
            obs.updateUserList(list);
        }
    }
    
    @Override
    public void notifyObserverCurrentUser(int user){
        for(UserObserver obs : listUserObserver){
            obs.updateCurrentUser(user);
        }
    }
    
    @Override
    public void notifyAtivityLineObserver(boolean activity){
        for(ActivityObserver obs : listActivityObserver){
            obs.updateActivity(activity);
        }
    }
    
    @Override
    public void removeConnObserver() {
        listConnObserver = new ArrayList<ListObserver>();
    }
    
    @Override
    public void removeUserObserver() {
        listUserObserver = new ArrayList<UserObserver>();
    }
    
    @Override
    public void addAtivityLineObserver(ActivityObserver obs) {
        this.listActivityObserver.add(obs);
    }

    @Override
    public void removeAtivityLineObserver() {
 
    }
    
//******************************************************************************
//**********************FIN METHODES OBSERVER***********************************
//******************************************************************************
    
    
    /**
    *<b>Permet d'obtenir la liste des utilisateurs connu</b><br> 
    * @return List
    */
    public List<User> getListUser(){
        return UserList;
    }
    
    /**
    *<b>Permet d'ajouter un utilisateur à la liste des utilisateurs</b><br>
    *Une fois l'utilisateur ajouté la methode notifie le controleur que la liste a changé<br>
    * et sauvegarde la nouvelle liste obtenu<br>
    * @param name
    */
    public void addUserToList(String name){
        UserList.add(new User(name));
        UpdateList();
        sauvegarde();
    }
    
    /**
    *<b>Permet de supprimer un utilisateur de la liste des utilisateurs</b><br>
    * Une fois l'utilisateur supprimé la methode notifie le controleur que la liste a changé<br>
    * et sauvegarde la nouvelle liste obtenu<br>
    * @param u
    */
    public void delUserToList(User u){        
        UserList.remove(u);
        UpdateList();
        sauvegarde();
    }
    
    /**
    *<b>Retourne la conection portant l'IP passe en paramtere. Liste cible: Liste d'attente </b><br>
    * Si la connexion n'est pas dans la liste d'attente la methode retourne NULL<br>
    * @param ID
    * @return Connection
    */
    public Connection getWaitingConnection(long ID){
        for ( Connection c : waitingList ){
            if ( c.getID() == ID ) 
                return c;
        }
        return null;
    }
    
    public void stopAllActive()
    {
        for (Connection c : activeList) {
            c.stop();
        }
        /*
        if (!activeList.isEmpty()) {
            RemoveConnActList(activeList.get(0));
        }*/
    }

    public soundSetting getLocalSoundSetting() {
        return localSoundSetting;
    }

    public int getPortUDP() {
        return portUDP;
    }

    public void setPortUDP(int portUDP) {
        this.portUDP = portUDP;
    }
  
    
    
    
    public void disableAutodiscover()
    {
        enableAutodiscover = false;
    }
    
    public void enableAutodiscover()
    {
        enableAutodiscover = true;
    }

    public boolean isEnableAutodiscover() {
        return enableAutodiscover;
    }
    
    
    
    public long getFirstID(){
        if ( !waitingList.isEmpty() )
        {
            return waitingList.get(0).getID();        
        }
        else if ( !activeList.isEmpty() )
        {
            return activeList.get(0).getID();        
        }
        else
        return 0;
    }
    
    /**
    *<b>Déplace le numero d'identification d'une connexion de la wait list vers l'active list </b><br>
    * La methode commence par parcourir la wait list pour trouver la connexion qui correspond à l'ID passé en parametre<br>
    * ensuite, la methode ajoute la connexion à la l'active list<br>
    * puis, supprime cette même connexion de la wait list<br>
    * @param ID
    */
    public void putConWaitToActiv(long ID){  
        Connection del = null;

        for ( Connection c : waitingList ){
            if ( c.getID() == ID ){
                activeList.add(c);
                del = c;
            }
        }
        //if ( del != null ) RemoveConnWaitList(del);
        UpdateList();
    }
    
    /**
    *<b>Déplace le numero d'identification d'une connexion de l'active list vers la wait list </b><br>
    * La methode commence par parcourir l'active list pour trouver la connexion qui correspond à l'ID passé en parametre<br>
    * ensuite, la methode ajoute la connexion à la waiting list<br>
    * puis, supprime cette même connexion de l'active list<br>
    * @param ID
    */
    public void putConActivToWait(long ID){       
        for ( Connection c : activeList ){
            if ( c.getID() == ID ){
                waitingList.add(c);
                //activeList.remove(c);
                //RemoveConnActList(c);
            }
        } 
        UpdateList();
    }
    
    /**
    *<b>Retourne la conection ayant l'ID passé en parametre. Liste cible: Liste active</b><br>
    * Si la connexion n'est pas dans la liste d'attente la methode retourne NULL<br>
    * @param ID
    * @return Connection
    */
    public Connection getActiveConnection(long ID){
        for ( Connection c : activeList ){
            if ( c.getID() == ID ) 
                return c;
        }
        System.out.println("ID non trouve");
        return null;
    }
       
    public Connection getConnection(long ID){
        for ( Connection c : activeList ){
            if ( c.getID() == ID ) 
                return c;
        }
        for ( Connection c : waitingList ){
            if ( c.getID() == ID ) 
                return c;
        }
        System.out.println("ID non trouve");
        return null;
        
    }
    
    public ArrayList<Long> getListConectionID()
    {
        ArrayList<Long> listID = new ArrayList<Long>();
        for ( Connection c : activeList ){
            listID.add(c.getID());
        }
        for ( Connection c : waitingList ){
            listID.add(c.getID());
        }
        return  listID;
    }
        
    public String getWaitingID(){  
        String a="";
        for ( Connection c : waitingList ){
            a+= " " + c.getID();
        } 
        return a;
    }
      
    public long getFirstActiveID(){  
        
        if (!activeList.isEmpty()) {
            return activeList.get(0).getID();
        } else {
            return 0;
        }
    }
    
    public void modifyConnectionStatus(long ID, Constant.Status status){
        for ( Connection c : activeList ){
            if ( c.getID() == ID ) 
                c.setID(ID);
        }
        for ( Connection c : waitingList ){
            if ( c.getID() == ID ){
                c.setID(ID);
            }
        }
    }
    
    /**
    *<b>définit le nom d'utilisateur d'une connexion passé en parametres</b><br>
    * la methode cherche l'ID de connexion dans l'active liste <br>
    * puis utilise la methode setUser() avec le nom passé en parametre<br>
    * @param ID 
    * @param user 
    */
    public void setUserToConn(long ID, String user){
        for ( Connection c : activeList ){
            if ( c.getID() == ID ) c.setUser(user);
        }
        UpdateList();
    }
    
    public User getCurrentUser(){
        if ( ! UserList.isEmpty() )
            return UserList.get(currentUser);
        else
            return null;
    }
    
    public void setCurrentUser(int u){
        currentUser = u;
        notifyObserverCurrentUser(currentUser);
    }
    
    private void sauvegarde(){
        try {

            ListeUtilisateur lu = new ListeUtilisateur(UserList);
            FileOutputStream fichier = new FileOutputStream("list.user");
            ObjectOutputStream oos = new ObjectOutputStream(fichier);
            oos.writeObject(lu);
            oos.flush();
            oos.close();
    
        } catch (java.io.IOException e) { e.printStackTrace(); }
    }
    
    private void ouvrir() throws FileNotFoundException, IOException, ClassNotFoundException{
        try {

        FileInputStream fichier = new FileInputStream("list.user");
        ObjectInputStream ois = new ObjectInputStream(fichier);
        ListeUtilisateur lu = (ListeUtilisateur) ois.readObject();
        UserList.clear();
        UserList.addAll(lu.UserList);
        
        } catch (java.io.IOException e) { e.printStackTrace(); }
    }

    // Retourne le numero de conexion et l'incremente de 1
    // Garantit ainsi l'unicite du numero retourné
    public long getNewConnectionID() {        
        return System.currentTimeMillis();
    }
    
    public boolean isLineActive(){
        return lineActive;
    }
    
    public void setLineActivity(boolean line){
        this.lineActive = line;
        notifyAtivityLineObserver(lineActive);
    }
    
    
}
